#include <iostream>
#include <fstream>
#include "TinyXML2.h"
#include "ModelFactory.h"
#include "Library.h"
#include "IRenderer.h"
#include "MS3DModelLoader.h"
#include "Settings.h"

Pointer<Model> CreateModelFromMdl( MdlFile& file )
{
	Pointer<Model> model = new Model();
	unsigned int meshCount = file.meshes.size();
	model->getMeshes().resize(meshCount);
	VertexType::Vector_t params;
	model->getSkeletonName() = file.skeletonName;

	for (unsigned int i = 0; i < meshCount; i++)
	{
		for (unsigned int j = 0; j < file.meshes[i].parameters.size(); ++j)
		{
			params.push_back(VertexType( file.meshes[i].parameters[j].m_offset,
										 file.meshes[i].parameters[j].m_size,
										 (VertexParameter)file.meshes[i].parameters[j].m_parameter ));
		}
		
		model->getMeshes()[i] = new Mesh(Pointer<IVertexContainer>(new IVertexContainer(VertexDeclaration(params))));

		for (unsigned int j = 0; j < file.meshes[i].attributeNames.size(); ++j)
		{
			int attributeIndex;
			attributeIndex = Renderer->GetAttributeLocationByName(file.meshes[i].material.shaderName, HashedString(file.meshes[i].attributeNames[j]));
			model->getMeshes()[i]->getVertexContainer()->AttributeIndex().push_back(attributeIndex);
		}

		memcpy(model->getMeshes()[i]->getMaterial().m_ambient, file.meshes[i].material.ambient, sizeof(float) * 4);
		memcpy(model->getMeshes()[i]->getMaterial().m_diffuse, file.meshes[i].material.diffuse, sizeof(float) * 4);
		memcpy(model->getMeshes()[i]->getMaterial().m_emmisive, file.meshes[i].material.emmisive, sizeof(float) * 4);
		memcpy(model->getMeshes()[i]->getMaterial().m_specular, file.meshes[i].material.specular, sizeof(float) * 4);
		model->getMeshes()[i]->getMaterial().m_shader = file.meshes[i].material.shaderName;
		model->getMeshes()[i]->getMaterial().m_shininess = file.meshes[i].material.shininess;

		model->getMeshes()[i]->getVertexContainer()->resize(file.meshes[i].vertexCount);
		memcpy(model->getMeshes()[i]->getVertexContainer()->getVertexPointer(), &(file.meshes[i].vertexData[0]), file.meshes[i].vertexCount * model->getMeshes()[i]->getVertexContainer()->getVertexDeclaration().m_vertexSize);
		
		std::vector<float>& testing1 = *(std::vector<float>*)(&file.meshes[i].vertexData);
		std::vector<float> testing2;
		testing2.resize(file.meshes[i].vertexData.size() / 4.0f);
		memcpy(&(testing2[0]), model->getMeshes()[i]->getVertexContainer()->getVertexPointer(), file.meshes[i].vertexData.size());
	}
	if (Library::getSingletonPtr())
	{
		Pointer<IResource> resource;
		CopyPointer(model, resource);
		Library::getSingleton().AddResource(file.modelName, resource);
	}
	return model;
}

Pointer<ModelInstance> CreateModelInstance( Pointer<Model>& model )
{
	Pointer<ModelInstance> modelInstance = new ModelInstance(model);

	//if( model->getSkeletonName().getString().length() > 0)
	//{
	//	Pointer<Skeleton> skeleton;
	//	Pointer<IResource> resource = Library::getSingleton().getResource(Skeleton::resourceType, model->getSkeletonName());
	//	CopyPointer(resource, skeleton);
	//	modelInstance->getSkeletonInstance() = CreateSkeletonInstance(skeleton);
	//}

	// Iterate through each mesh
	for (Mesh::Vector_t::iterator mesh = model->getMeshes().begin(); mesh != model->getMeshes().end(); ++mesh)
	{
		MeshInstance meshInstance( *mesh );
		
		modelInstance->getMeshInstances().push_back(meshInstance);
	}
	return modelInstance;
}

struct GraphicResourceAnimation
{
	typedef std::vector<GraphicResourceAnimation> Vector_t;

	std::string name;
	unsigned int priority;
	float startTime;
	float endTime;
	bool loop;
};

bool LoadGraphicResourceXML( std::string filename )
{
	std::string shaderName;
	std::string modelFileName;
	std::vector<std::string> attributeNames;
	GraphicResourceAnimation::Vector_t animationResources;

	tinyxml2::XMLDocument doc;
	if (doc.LoadFile(filename.c_str()) == tinyxml2::XML_ERROR_FILE_NOT_FOUND)
		return false;

	tinyxml2::XMLElement* rootNode = doc.RootElement();

	if (strcmp(rootNode->Name(),"Graphic"))
		return false;
	
	tinyxml2::XMLElement* model = rootNode->FirstChildElement("Model");
	tinyxml2::XMLElement* shader = rootNode->FirstChildElement("Shader");
	tinyxml2::XMLElement* animations = rootNode->FirstChildElement("Animations");

	if (!shader)
		shaderName = "nil";
	else
		shaderName = shader->Attribute("Name");

	if (!model)
		return false;

	modelFileName = model->Attribute("Filename");
	
	for (tinyxml2::XMLElement* ele = model->FirstChildElement("Attribute"); ele != NULL; ele = ele->NextSiblingElement("Attribute"))
		attributeNames.push_back(ele->Attribute("Name"));

	if (animations)
	{
		for (tinyxml2::XMLElement* ele = animations->FirstChildElement("Animation"); ele != NULL; ele = ele->NextSiblingElement("Animation"))
		{
			GraphicResourceAnimation resource;
			resource.name = ele->Attribute("Name");
			resource.priority = ele->UnsignedAttribute("Priority");
			resource.startTime = ele->FloatAttribute("Start");
			resource.endTime = ele->FloatAttribute("End");
			resource.loop = ele->BoolAttribute("Loop");
			animationResources.push_back(resource);
		}
	}
	
	MdlFile modelFile;

	int start = modelFileName.find_last_of('.');
	
	if (modelFileName.substr(start) == std::string(".ms3d"))
	{
		msModel msmodel;
		msmodel.Load((Settings::getSingleton().getDirectory() + "//Data//Models//" + modelFileName).c_str());
		ConvertToMdlFile(msmodel, modelFile);
	} else if (modelFileName.substr(start) == std::string(".mdl"))
	{
		LoadMdlFile(Settings::getSingleton().getDirectory() + "//Data//Models//" + modelFileName, modelFile);
	}

	for (unsigned int i = 0; i < modelFile.meshes.size(); ++i)
	{
		modelFile.meshes[i].material.shaderName = shaderName;
		modelFile.meshes[i].attributeNames = attributeNames;
	}

	Pointer<Model> ptrmodel = CreateModelFromMdl(modelFile);
	
	return true;
}